#pragma once
#include "Http.h"
#include "BodyHttpRequest.h"
#include "HeaderHttpRequest.h"
#include "EasyHttpProxy.generated.h"

namespace EEasyHttpRequestStage
{
	enum Type
	{
		eStage_NotStart,
		eStage_HeaderPause,
		eStage_HeaderCanceled,
		eStage_HeaderProcessing,
		eStage_HeaderFailed,
		eStage_BodyProcessing,
		eStage_BodyPause,
		eStage_BodyCanceled,
		eStage_BodyFailed,
		eStage_BodyOutOfDate,
		eStage_Succeed,
	};

	const TCHAR* ToString(const Type eStage);
}

DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FEasyHttpProxyFinishDelegate, const FString&, Url, bool, bSucceed);

UCLASS(BlueprintType)
class EASYHTTP_API UEasyHttpProxy : public UObject
{
	GENERATED_BODY()
public:
	UEasyHttpProxy(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
	~UEasyHttpProxy();

public:
	/**
	 * return:
	 * true ---the onfinish event will brocast.
	 * false---the onfinish event will not brocast.
	 */
	UFUNCTION(BlueprintCallable, Category = "Request Header")
		bool DoRequest(const FString &Url, const FString &SaveFileName, const FString &HashCode, bool bLoadTemporary = true, bool bAutoRetryOutOfDate = true);
	UFUNCTION(BlueprintCallable, Category = "Request Header")
		bool Retry();

	UFUNCTION(BlueprintCallable, Category = "Request Header")
		void Pause();
	UFUNCTION(BlueprintCallable, Category = "Request Header")
		void Resume();

	UFUNCTION(BlueprintCallable, Category = "Request Header")
		void Cancel();

	UFUNCTION(BlueprintCallable, Category = "Request Header")
		void Destroy();

	UFUNCTION(BlueprintCallable, Category = "Request Header")
		int32 GetRecvBytes() const;
	UFUNCTION(BlueprintCallable, Category = "Request Header")
		int32 GetTotalBytes() const;

	UFUNCTION(BlueprintCallable, Category = "Request Header")
		bool Save(bool bSaveTemporary = true);

	UPROPERTY(BlueprintAssignable, Category = "Request Header")
		FEasyHttpProxyFinishDelegate OnFinishEvent;

public:
	bool RequestHeader();
	void RequestBody();

	bool RetryRequestHeader();
	bool RetryRequestBody();

private:
	bool SaveFile();
	bool SerializedSave();

private:
	void CompleteCallback(int32 UniqueID, FHttpRequestPtr Request, FHttpResponsePtr Response, bool bConnectedSuccessfully);
	void HeaderReceivedCallback(int32 UniqueID, FHttpRequestPtr Request, const FString& HeaderName, const FString& NewHeaderValue);
	void ProgressCallback(int32 UniqueID, FHttpRequestPtr Request, int32 BytesSent, int32 BytesReceived);
	void WillRetryCallback(int32 UniqueID, FHttpRequestPtr Request, FHttpResponsePtr Response, float SecondsToRetry);
	void FinishCallback(int32 UniqueID);

	void HeaderCompleteCallback(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bConnectedSuccessfully);
	void HeaderHeaderReceivedCallback(FHttpRequestPtr Request, const FString& HeaderName, const FString& NewHeaderValue);
	void HeaderProgressCallback(FHttpRequestPtr Request, int32 BytesSent, int32 BytesReceived);
	void HeaderWillRetryCallback(FHttpRequestPtr Request, FHttpResponsePtr Response, float SecondsToRetry);
	void HeaderFinishCallback();

	void BodyCompleteCallback(FBodyHttpRequest &BodyRequest, FHttpRequestPtr Request, FHttpResponsePtr Response, bool bConnectedSuccessfully);
	void BodyHeaderReceivedCallback(FBodyHttpRequest &BodyRequest, FHttpRequestPtr Request, const FString& HeaderName, const FString& NewHeaderValue);
	void BodyProgressCallback(FBodyHttpRequest &BodyRequest, FHttpRequestPtr Request, int32 BytesSent, int32 BytesReceived);
	void BodyWillRetryCallback(FBodyHttpRequest &BodyRequest, FHttpRequestPtr Request, FHttpResponsePtr Response, float SecondsToRetry);
	void BodyFinishCallback(int32 UniqueID);

private:
	void BindBaseRequestEvents(FBaseHttpRequest &Request);
	void UnbindBaseRequestEvents(FBaseHttpRequest &Request);

private:
	void BroadcastFinish(bool bSucceed);

private:
	void UnbindHeaderRequestEvents();
	void UnbindBodyRequestEvents();

	void Reset();
	void ResetHeader();
	void CleanBodyRequest();

private:
	bool DeserializedFromFile(const FString &SaveFileName);
	bool DeserializedFromBuffer(FBufferReader &Reader);
	void SerializedToBuffer(FMemoryWriter &Writer);
	bool SerializedToFile(const FString &SaveFileName);

	bool CanSerializedSave() const;
	bool IsEasyRequsetSucceed() const;

private:
	FString GetFullPathFileName() const;

private:
	void FixStage();

private:
	const TCHAR* GetStageStr() const { return EEasyHttpRequestStage::ToString(m_eStage); }

private:
	FTimerManager* GetTimeManager() const;
	void CheckRequestBodyResult();
	void RegisterNextFrameCheckRequestBodyResult();

private:
	FString m_Url;
	FString m_HashCode;
	FString m_SaveFileName;
	bool m_bNextFrameCheck;
	bool m_bAutoRetryOutOfDate;
	int32 m_nOutOfDateAutoRetryNum;
	EEasyHttpRequestStage::Type m_eStage;

	FHeaderHttpRequest m_pHeaderRequest;
	TArray<FBodyHttpRequest> m_BodyRequests;
};
